package com.fingard.dsp.bank.directbank.cmb01;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fingard.FGBiz;
import com.fingard.constant.Format;
import com.fingard.dsp.bank.dbobj.receipt.ReceiptMapping;
import com.fingard.dsp.bank.directConfig.BankConfig;
import com.fingard.dsp.bank.directbank.DirectBase;
import com.fingard.dsp.bank.directbank.FailCode;
import com.fingard.dsp.bank.directbank.chinapay01.util.LogUtil;
import com.fingard.dsp.bank.helpers.DataFileHelper;
import com.fingard.io.FileHelper;
import com.fingard.net.ITcpClient;
import com.fingard.net.TcpPortClient;
import com.fingard.net.TcpReceiver;
import com.fingard.net.WebRequest;
import com.fingard.text.StringHelper;
import com.fingard.text.UrlHelper;
import com.fingard.xml.XmlTextReader;
import com.fingard.xml.XmlTextWriter;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.*;
import java.io.*;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.X509EncodedKeySpec;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class CMB01Base extends DirectBase implements ITcpClient {
    private static HashMap<String, BankConfig> hashBankConfig = new HashMap<String, BankConfig>();
    private static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    //返回22位的业务参考号
    protected String getYURREF(String transSeqID) {
        int supplen = 22 - transSeqID.length();
        return String.format("%0" + supplen + "d", 0) + transSeqID;
    }

    protected void writeCommonHeader(XmlTextWriter xmlWriter, CMB01Header p_bankHeader) throws Exception {
        if (StringHelper.isNullOrEmpty(p_bankHeader.FUNNAM)) {
            throw new Exception("未赋值FUNNAM");
        }
        if (StringHelper.isNullOrEmpty(bankFront.loginName)) {
            throw new Exception("未设置登录名");
        }
        xmlWriter.writeStartDocByDeclaration("<?xml version=\"1.0\" encoding=\"GBK\"?>");
        xmlWriter.writeStartElement("CMBSDKPGK");
        xmlWriter.writeStartElement("INFO");
        xmlWriter.writeElementString("FUNNAM", p_bankHeader.FUNNAM);
        xmlWriter.writeElementString("DATTYP", "2");
        xmlWriter.writeElementString("LGNNAM", bankFront.loginName);
        xmlWriter.writeEndElement();

    }

    protected String getCharset() {
        return getBankConfig().getCharset("GBK");
    }

    protected String[] sendToBank(String p_reqStr) throws Exception {
        return sendToBank(p_reqStr, "", "");
    }

    protected String[] sendToBank(String p_reqStr, String transType, String commandCode) throws Exception {
        String[] retStr = new String[]{"", ""};

        String requestType = this.getBankConfig().getValueAsString(
                "requestType");
        String tmpCharset = getCharset();

        int readTimeout = getReadTimeout();
        WriteBankLogLn("readTimeout:" + readTimeout / 1000 + "秒");
        if (StringHelper.hasAnyChar(transType) && StringHelper.hasAnyChar(commandCode)) {
            this.detailSimulateForBank(bankFront.bankCode, transType, commandCode, p_reqStr, retStr);
            if (retStr[1].length() > 0) {
                return retStr;
            }
        }
        if (requestType.toLowerCase().equals("socket")) {
            if (StringHelper.isNullOrEmpty(bankFront.serverIP)
                    && StringHelper.isNullOrEmpty(bankFront.serverPort)
                    && StringHelper.hasAnyChar(bankFront.serverURL)) {
                String[] ipport = UrlHelper
                        .pickupIpAndPort(bankFront.serverURL);
                if (StringHelper.hasAnyChar(ipport[0])
                        && StringHelper.hasAnyChar(ipport[1])) {
                    bankFront.serverIP = ipport[0];
                    bankFront.serverPort = ipport[1];
                }
            }
            WriteBankLogLn("2.提交银行的地址：" + bankFront.serverIP + ":"
                    + bankFront.serverPort + ",requestType:" + requestType);
            if (StringHelper.isNullOrEmpty(bankFront.serverIP)
                    || StringHelper.isNullOrEmpty(bankFront.serverPort)) {
                retStr[0] = FailCode.E9981;
                retStr[1] = "未设置前置机的serverIP或serverPort";
            } else {
                TcpPortClient tmpClient = new TcpPortClient(bankFront.serverIP,
                        Integer.parseInt(bankFront.serverPort), this);
                tmpClient.timeoutMil = readTimeout;
                byte[] tmpBytesBody = p_reqStr.getBytes(tmpCharset);
                String tmpHead = String.format("%-8s", tmpBytesBody.length);

                byte[] tmpBytesHead = tmpHead.getBytes(tmpCharset);

                byte[] tmpBytesSend = new byte[tmpBytesHead.length
                        + tmpBytesBody.length];
                System.arraycopy(tmpBytesHead, 0, tmpBytesSend, 0,
                        tmpBytesHead.length);
                System.arraycopy(tmpBytesBody, 0, tmpBytesSend,
                        tmpBytesHead.length, tmpBytesBody.length);

                WriteBankLogStep2(tmpHead + p_reqStr);
                TcpReceiver tmpReceiver = tmpClient.sendMessage(tmpBytesSend);
                if (StringHelper.isNullOrEmpty(tmpReceiver.connState)) {
                    String tmpBkStr = new String(tmpReceiver.recBytes, 0,
                            tmpReceiver.readedLength, tmpCharset);
                    WriteBankLogStep3(tmpBkStr);
                }
                retStr[0] = tmpReceiver.connState;
                if (tmpReceiver.isCompleted) {
                    retStr[1] = new String(tmpReceiver.recBytes,
                            tmpReceiver.contentStartIndex,
                            tmpReceiver.contentLength, tmpCharset);
                } else {
                    retStr[1] = tmpReceiver.getConnMsg();
                    WriteBankLogLn(retStr[1]);
                }
            }
        } else {
            WriteBankLogLn("2.提交银行的地址：" + bankFront.serverURL + ",requestType:"
                    + requestType);
            WriteBankLogStep2(p_reqStr);

            WebRequest tmpWebRequest = new WebRequest(bankFront.serverURL);
            tmpWebRequest.setLogCode(this.getLogCode());
            tmpWebRequest.setBankFront(this.bankFront);
            tmpWebRequest.setConnectTimeout(10000);

            tmpWebRequest.setReadTimeout(readTimeout);
            tmpWebRequest.setRequestProperty("connection", "Close");

            byte[] tmpUpBytes = p_reqStr.getBytes(tmpCharset);
            retStr = tmpWebRequest.upload(tmpUpBytes, tmpCharset);
            WriteBankLogStep3(retStr[1]);
            if (retStr[0].length() == 0) {
                int tmpIndex = retStr[1].indexOf("<");
                if (tmpIndex > 0) {
                    retStr[1] = retStr[1].substring(tmpIndex);
                }
            }
        }
        return retStr;
    }

    /**
     * 发送并获取返回
     *
     * @param actItem     商户配置信息
     * @param bizContent  业务JSON
     * @param serviceCode 当前服务
     * @return 响应JOSN
     * @throws Exception 服务器请求异常
     */
    public String[] sendToBank(JSONObject tmpReqJson, String appId, String appSecret, String url) throws Exception {
        String[] tmpStrRet = new String[]{"", ""};
        String tmpReqMsg = JSON.toJSONString(tmpReqJson);
        String sign = sha(tmpReqMsg.getBytes(), "MD5");
        //拼接参数
        Map<String, Object> map = new HashMap<String, Object>();
        String timestamp = String.valueOf(new Date().getTime() / 1000);
        map.put("appid", appId);
        map.put("secret", appSecret);
        map.put("timestamp", timestamp);
        map.put("sign", sign);

        String apiString = convertMapToStr(map);
        WriteBankLogLn("apisign待加密值为：" + apiString);
        String apisign = sha(apiString.getBytes(getCharset()), "MD5");
        WriteBankLogLn("apisign值为：" + apisign);

        CloseableHttpClient httpclient = getHttpsClient();
        WriteBankLogLn("发送银行的地址为：" + url);
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("appid", appId);
        httpPost.addHeader("timestamp", timestamp);
        httpPost.addHeader("sign", sign);
        httpPost.addHeader("apisign", apisign);
        httpPost.addHeader("verify", "MD5Verify");
        httpPost.addHeader("Content-Type", "application/json");
        CloseableHttpResponse response = null;
        try {
            StringEntity dataEntity = new StringEntity(tmpReqMsg, ContentType.APPLICATION_JSON);
            httpPost.setEntity(dataEntity);
            response = httpclient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                tmpStrRet[1] = EntityUtils.toString(entity, getCharset());
            }
            WriteBankLogStep3(tmpStrRet[1]);
        } catch (IOException ex) {
            ex.printStackTrace();
            throw ex;
        } finally {
            try {
                if (httpclient != null) {
                    httpclient.close();
                }
                if (response != null) {
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return tmpStrRet;
    }

    /**
     * 将MAP转换为post请求串
     *
     * @return
     */
    public String convertMapToStr(Map<String, Object> params) {
        String reqStr = "";
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);
        for (String key : keys) {
            if (!params.get(key).equals("")) {
                if (reqStr.length() > 0) {
                    reqStr += "&";
                }
                reqStr += key + "=" + params.get(key);
            }
        }
        return reqStr;
    }


    /**
     * @param data 待计算的数据
     * @param type 算法
     * @return 计算结果
     */
    protected String sha(byte[] data, String type) {
        MessageDigest md = null;
        try {
            md = MessageDigest.getInstance(type);
            md.reset();
            md.update(data);
            byte[] bytes = md.digest();
            char chars[] = new char[32];
            for (int i = 0; i < chars.length; i = i + 2) {
                byte b = bytes[i / 2];
                chars[i] = HEX_CHARS[(b >>> 0x4) & 0xf];
                chars[i + 1] = HEX_CHARS[b & 0xf];
            }
            return new String(chars);
        } catch (Exception e) {
            LogUtil.writeErrorLog(type + "计算失败", e);
            return null;
        }
    }

    public static CloseableHttpClient getHttpsClient() {
        CloseableHttpClient httpClient = null;
        SSLContext context;
        try {
            context = SSLContext.getInstance("SSL");
            context.init(null, new TrustManager[] {new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString)
                        throws CertificateException {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString)
                        throws CertificateException {
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

            }}, new SecureRandom());

            HostnameVerifier verifier = new HostnameVerifier() {
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            };
            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(context, verifier);
            httpClient = HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return httpClient;
    }

    protected CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                return true;
            }
        }).build();
        SSLConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(sslContext, new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        });
        return HttpClients.custom().setSSLSocketFactory(sslSF).build();
    }

    /*
     * 企业端使用SOCKET协议，通过以下数据包格式提交XML报文数据到直联前置程序，完成报文数据交换。
     * 通讯数据包格式为：8位字节报头（XML报文长度，左对齐，长度不够右补空格) + XML报文。
     * 范例：假如XML报文长度为1300个字节，通讯数据包格式为：“1300 ”（右边4个空格） + XML报文。
     */
    public void getContentLength(TcpReceiver p_receiver) throws Exception {
        if (p_receiver.contentLength == -1) {
            if (p_receiver.readedLength >= 8) {
                String tmpHead = new String(p_receiver.recBytes, 0, 8,
                        getCharset());
                p_receiver.contentLength = Integer.parseInt(tmpHead.trim());
                p_receiver.contentStartIndex = 8;
            }
        }
    }

    public void copyFile(String src, String target) throws IOException {
        InputStream in = null;
        FileOutputStream out = null;
        File srcFile = new File(src);
        File targetFile = new File(target);
        try {
            in = new FileInputStream(srcFile);
            out = new FileOutputStream(targetFile);
            byte[] bytes = new byte[1024];
            int len = -1;
            while ((len = in.read(bytes)) != -1) {
                out.write(bytes, 0, len);
            }
            in.close();
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }

    //上送格式为json格式
    protected String[] sendToATS(String jsonData) throws Exception {
        String[] retStr = new String[]{"", ""};
        WriteBankLogLn("2.提交ATS的地址：" + FGBiz.dspCenter.dspSet.receiptPushServerUrl);

        //发送银行前置
        WebRequest tmpRequest = new WebRequest(FGBiz.dspCenter.dspSet.receiptPushServerUrl);
        tmpRequest.setConnectTimeout(10000);
        tmpRequest.setRequestProperty("connection", "Close");
        tmpRequest.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        String charsetName = getCharset();
        StringBuilder data = new StringBuilder();
        data.append(jsonData);
        WriteBankLogLn("3.推送给ATS的数据" + data.toString());
        byte[] tmpWriteBytes = data.toString().getBytes(charsetName);
        retStr = tmpRequest.upload(tmpWriteBytes, charsetName);
        WriteBankLogLn("4.返回的数据" + retStr[1]);

        return retStr;
    }


    /**
     * 账单类型映射
     */
    protected String convertBillTypeToBank(String p_billTypeCode) throws Exception {
        return getBankBillTypeConfig().getMapValueByKey("billTypeMapping", p_billTypeCode);
    }

    public BankConfig getBankBillTypeConfig() {
        String cfgKey = reqHeader.bankCode + "-" + "billType.xml";
        if (!hashBankConfig.containsKey(cfgKey)) {
            BankConfig tmpBankConfig = retrieveBankConfig(reqHeader.bankCode, "billType.xml");
            hashBankConfig.put(cfgKey, tmpBankConfig);
        }
        return hashBankConfig.get(cfgKey);
    }

    public static Map<String, String> convertToMap(String srcStr) {
        Map<String, String> params = new HashMap<String, String>();

        String[] nodes = srcStr.split("&");
        for (String node : nodes) {
            int index = node.indexOf("=");

            String key = node.substring(0, index);
            String value = URLDecoder.decode(node.substring(index + 1));
            params.put(key, value);
        }

        return params;
    }

    /**
     * 组织待验签数据
     *
     * @param noticeData
     * @return
     */
    public String getVerifyData(Map<String, String> noticeData) {
        String[] keys = noticeData.keySet().toArray(new String[]{});
        String[] realkeys = new String[keys.length - 1];
        Arrays.sort(keys);
        /**
         * 执行数组拷贝
         * System.arraycopy(源数组，源数组的起始位置，目标数组，目标数组的起始位置，拷贝多少位)
         */
        System.arraycopy(keys, 1, realkeys, 0, keys.length - 1);
        StringBuilder buffer = new StringBuilder();
        for (String key : realkeys) {
            String value = noticeData.get(key);
            buffer.append("&").append(key).append("=").append(value);
        }
        return buffer.substring(1);
    }

    /**
     * 通知数据验签
     * strToSign: getVerifyData获取的数据
     * strSign:signature的值
     * publicKey:验签公钥
     */
    public boolean isValidSignature(String strToSign, String strSign, String publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = Base64.decodeBase64(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));

            Signature signature = Signature.getInstance("SHA1WithRSA");

            signature.initVerify(pubKey);
            signature.update(strToSign.getBytes("GBK"));

            boolean bverify = signature.verify(Base64.decodeBase64(strSign));
            return bverify;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 处理回单数据
     *
     * @param fdate      回单日期
     * @param printFlags 打印标识
     * @param actNum     账号
     * @return
     */
    public Map<String, ReceiptMapping> dealWithReceiptXml(String fdate, String[] printFlags, String actNum) throws Exception {
        StringBuilder sbd = new StringBuilder();
        int count = 0;
        Map<String, ReceiptMapping> mapDetail = new HashMap<String, ReceiptMapping>();
        for (String printFlag : printFlags) {
            //取出银行响应报文
            String dataFilePath = new DataFileHelper(this).getBankReceiptDataFile(Format.Date8Format.parse(fdate), printFlag + "_" + actNum);
            File dataFile = new File(dataFilePath);
            if (dataFile.exists()) {
                WriteBankLogLn("账号" + actNum + "在" + fdate + "的" + printFlag + "回单文件" + dataFile + "存在，开始处理");
                String fcontent = FileHelper.readToString(dataFile, FGBiz.dspCenter.charset);
                XmlTextReader xmlReader = new XmlTextReader(fcontent);

                while (xmlReader.readByParentPath("/CMBSDKPGK")) {
                    String ISTNBR = StringHelper.getFirstNotEmpty(xmlReader.readedXElement.getSingleText("ISTNBR"), xmlReader.readedXElement.getSingleText("PRTINS"));//打印实例号
                    if (StringHelper.hasAnyChar(ISTNBR)) {//只要有打印实例号
                        String[] refList = new String[7];
                        refList[0] = xmlReader.readedXElement.getSingleTextNotNull("NARTX1");//对应历史明细YURREF
                        refList[1] = xmlReader.readedXElement.getSingleTextNotNull("SEQNBR");
                        refList[2] = xmlReader.readedXElement.getSingleTextNotNull("BUSNBR");//对应历史明细NAREXT
                        refList[3] = xmlReader.readedXElement.getSingleTextNotNull("TRSREF");
                        refList[4] = xmlReader.readedXElement.getSingleTextNotNull("TRXSET");
                        refList[5] = xmlReader.readedXElement.getSingleTextNotNull("TRSNBR");
                        refList[6] = xmlReader.readedXElement.getSingleTextNotNull("TSKNBR");
                        //atses-13904 1、兼容超级网银取值逻辑
                        if ("NPTRFRCTX1".equals(xmlReader.readedXElement.name)) {
                            refList[0] = xmlReader.readedXElement.getSingleTextNotNull("TRSREF");
                        }
                        String firstStr = StringHelper.getFirstNotEmptyTrim(refList);//NARTX1,BUSNBR,SEQNBR,TRSREF
                        if (StringHelper.hasAnyChar(firstStr)) {
                            ReceiptMapping eachRcptMp = new ReceiptMapping();
                            eachRcptMp.busRef = firstStr;
                            for (int i = 0; i < refList.length; i++) {//潜在多个关联号也一并添加
                                if (!firstStr.equals(refList[i]) && StringHelper.hasAnyChar(refList[i])) {
                                    eachRcptMp.addOtherBusRef(refList[i], eachRcptMp.receiptNo);
                                }
                            }
                            eachRcptMp.bank = "308";
                            String eachAmt = "";
                            String[] amtColumns = new String[]{"TRSAMT", "INTAMT", "TRXAMT", "AMTTRS", "ACTAMT", "BILAMT", "OPNAMT", "TRXAM1", "FEEAMT"};
                            for (int i = 0; i < amtColumns.length; i++) {
                                eachAmt = xmlReader.readedXElement.getSingleTextNotNull(amtColumns[i]);
                                if (StringHelper.hasAnyChar(eachAmt)) {
                                    break;
                                }
                            }

                            if (StringHelper.isNullOrEmpty(eachAmt) && "SAINTLSBZ0".equals(xmlReader.readedXElement.name)) {
                                //利息金额
                                eachAmt = xmlReader.readedXElement.getSingleTextNotNull("INTRST");
                            } else if (StringHelper.isNullOrEmpty(eachAmt) && "EXICORRBZ1".equals(xmlReader.readedXElement.name)) {
                                //结汇
                                String[] amts = new String[]{"OUTAMT", "RCVAMT"};//OUTAMT卖出金额，RCVAMT买入金额
                                for (int i = 0; i < amts.length; i++) {
                                    String outRcvAmt = xmlReader.readedXElement.getSingleTextNotNull(amts[i]);
                                    if (StringHelper.hasAnyChar(outRcvAmt)) {
                                        BigDecimal decAmtOutRcv = new BigDecimal(outRcvAmt);
                                        BigDecimal decZero = new BigDecimal("0.00");
                                        if (decAmtOutRcv.compareTo(decZero) != 0) {
                                            eachAmt = outRcvAmt;
                                            break;
                                        }
                                    }
                                }
                            }
                            if (StringHelper.hasAnyChar(eachAmt)) {
                                eachRcptMp.amount = new BigDecimal(eachAmt);
                            }
                            eachRcptMp.setBusDate(Format.Date8Format.parse(fdate));
                            eachRcptMp.ownAct = actNum;
                            //CPRRCRTNX1 对公转账退票回单（批量）；CPRRCRCVX1收款
                            if ("CPRRCRTNX1".equals(xmlReader.readedXElement.name) || "CPRRCRCVX1".equals(xmlReader.readedXElement.name)) {
                                eachRcptMp.moneyWay = "2";
                            } else if ("CSTRFPRTX0".equals(xmlReader.readedXElement.name) || "AGNCBPAYZ1".equals(xmlReader.readedXElement.name) || "CPRRCSNDX1".equals(xmlReader.readedXElement.name)) {
                                eachRcptMp.moneyWay = "1";//付款
                            }
                            eachRcptMp.keyName = "busRef";
                            eachRcptMp.receiptNo = ISTNBR;
                            //增加对方户名
                            String[] oppActNames = new String[]{"RCVEAN", "RCVNAM", "BNFNAM", "ECLNAM", "TRXNM1"};
                            for (int i = 0; i < oppActNames.length; i++) {
                                String oppActName = xmlReader.readedXElement.getSingleTextNotNull(oppActNames[i]);
                                if (StringHelper.hasAnyChar(oppActName)) {
                                    eachRcptMp.oppActName = oppActName;
                                    break;
                                }
                            }
                            //增加对方账号
                            String[] oppActNums = new String[]{"RCVEAC", "BNFACC", "INSEAC", "RCVACC", "EBKACC"};
                            for (int i = 0; i < oppActNums.length; i++) {
                                String oppActNum = xmlReader.readedXElement.getSingleTextNotNull(oppActNums[i]);
                                if (StringHelper.hasAnyChar(oppActNum)) {
                                    eachRcptMp.oppActNum = oppActNum;
                                    break;
                                }
                            }
                            mapDetail.put(ISTNBR, eachRcptMp);
                        }
                    } else if (!"INFO".equals(xmlReader.readedXElement.name) && !"CSRRCFDFZ1".equals(xmlReader.readedXElement.name)) {
                        count++;
                        sbd.append("节点" + xmlReader.readedXElement.name + "无打印实例号<ISTNBR>&&");
                        //FGBiz.logTrace.infoNoTimeBank(reqHeader.bankCode, reqHeader.transType, "节点" + xmlReader.readedXElement.name + "无打印实例号<ISTNBR>");
                    }
                }
                WriteBankLogLn("账号" + actNum + "在" + fdate + "的" + printFlag + "回单文件" + dataFile + "处理结束，其中有" + count + "个节点处理失败，失败原因：" + sbd.toString());
            }
        }
        return mapDetail;
    }

    /**
     * fileList文件列表分组
     *
     * @return
     */
    public Map<Integer, List<ReceiptMapping>> avgGrouping(List<ReceiptMapping> listFile, int n) {
        Map<Integer, List<ReceiptMapping>> map = new HashMap<Integer, List<ReceiptMapping>>();
        //根据线程池进行分组：分组规则，
        int listSize = listFile.size();//集合总数
        int toIndex = listFile.size() / n;//商
        int keyToken = 1;
        if (toIndex <= 1) {
            map.put(keyToken, listFile);
        } else {
            for (int i = 0; i < listFile.size(); i += toIndex) {
                if (i + toIndex * 2 > listSize) {//最后的数据全部放入最后的集合中
                    toIndex = listSize - i;
                }
                List<ReceiptMapping> newList = listFile.subList(i, i + toIndex);
                map.put(keyToken, newList);
                keyToken++;
            }
        }
        return map;
    }

    /**
     * 通过线程池上传回单文件列表
     *
     * @param listFile
     * @return
     */
    public Map<String, ReceiptMapping> uploadFileByThreadPool(List<ReceiptMapping> listFile) throws Exception {
        Map<String, ReceiptMapping> mappingMap = new HashMap<String, ReceiptMapping>();
        //获取线程池核心数，默认为4
        int corePoolSize = StringHelper.isNullOrEmpty(getBankConfig().getValueAsString("corePoolSize")) ? 4
                : Integer.parseInt(getBankConfig().getValueAsString("corePoolSize"));
        //获取线程池最大数，默认为核心线程的2倍
        int maximumPoolSize = corePoolSize * 2;
        //创建一个线程池
        ThreadPoolExecutor executorService = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
                200, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(maximumPoolSize));
        //分组
        Map<Integer, List<ReceiptMapping>> map = avgGrouping(listFile, executorService.getCorePoolSize());
        WriteBankLogLn("*****************回单文件总数为" + listFile.size() + "，总共分为" + map.size() + "组*****************");
        WriteBankLogLn("*****************开启线程池开始上传文件*****************");
        //开启线程池上传文件
        for (Map.Entry<Integer, List<ReceiptMapping>> m : map.entrySet()) {
            CMB01ThreadPool thread = new CMB01ThreadPool(m.getKey(), m.getValue(), reqHeader.bankCode, getBankConfig());
            executorService.execute(thread);
            WriteBankLogLn("线程池中线程数目：" + executorService.getPoolSize() + "，队列中等待执行的任务数目：" +
                    executorService.getQueue().size() + "，已执行完别的任务数目：" + executorService.getCompletedTaskCount());
        }
        executorService.shutdown();
        //等待线程池线程全部结束，获取上传成功的回单
        while (true) {
            if (executorService.isTerminated()) {
                WriteBankLogLn(CMB01ThreadPool.getRunLog().toString());
                //获取上传成功的map
                mappingMap = CMB01ThreadPool.getMap();
                break;
            }
        }
        return mappingMap;
    }

    // 根据年月求每个月的月末日期
    public static String dateConvert(Date date) throws ParseException {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        int days = c.getActualMaximum(Calendar.DAY_OF_MONTH);
        c.add(Calendar.DAY_OF_MONTH, days - 1);
        Date date2 = c.getTime();
        String monthend = new SimpleDateFormat("yyyyMMdd").format(date2);
        return monthend;
    }

    //余额调节原因类型映射
    protected String convertDeDZToBank(String p_deDZATS) throws Exception {
        return getBankConfig().getMapValueByKey("DeDZMapping", p_deDZATS);
    }
}
